import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
df = pd.read_csv('kc-house-data.csv')
df.head()
| id | date | price | bedrooms | bathrooms | sqft_living | sqft_lot | floors | waterfront | view | ... | grade | sqft_above | sqft_basement | yr_built | yr_renovated | zipcode | lat | long | sqft_living15 | sqft_lot15 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7129300520 | 20141013T000000 | 221900.0 | 3 | 1.00 | 1180 | 5650 | 1.0 | 0 | 0 | ... | 7 | 1180 | 0 | 1955 | 0 | 98178 | 47.5112 | -122.257 | 1340 | 5650 |
| 1 | 6414100192 | 20141209T000000 | 538000.0 | 3 | 2.25 | 2570 | 7242 | 2.0 | 0 | 0 | ... | 7 | 2170 | 400 | 1951 | 1991 | 98125 | 47.7210 | -122.319 | 1690 | 7639 |
| 2 | 5631500400 | 20150225T000000 | 180000.0 | 2 | 1.00 | 770 | 10000 | 1.0 | 0 | 0 | ... | 6 | 770 | 0 | 1933 | 0 | 98028 | 47.7379 | -122.233 | 2720 | 8062 |
| 3 | 2487200875 | 20141209T000000 | 604000.0 | 4 | 3.00 | 1960 | 5000 | 1.0 | 0 | 0 | ... | 7 | 1050 | 910 | 1965 | 0 | 98136 | 47.5208 | -122.393 | 1360 | 5000 |
| 4 | 1954400510 | 20150218T000000 | 510000.0 | 3 | 2.00 | 1680 | 8080 | 1.0 | 0 | 0 | ... | 8 | 1680 | 0 | 1987 | 0 | 98074 | 47.6168 | -122.045 | 1800 | 7503 |
5 rows × 21 columns
df.describe()
| id | price | bedrooms | bathrooms | sqft_living | sqft_lot | floors | waterfront | view | condition | grade | sqft_above | sqft_basement | yr_built | yr_renovated | zipcode | lat | long | sqft_living15 | sqft_lot15 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 2.161300e+04 | 2.161300e+04 | 21613.000000 | 21613.000000 | 21613.000000 | 2.161300e+04 | 21613.000000 | 21613.000000 | 21613.000000 | 21613.000000 | 21613.000000 | 21613.000000 | 21613.000000 | 21613.000000 | 21613.000000 | 21613.000000 | 21613.000000 | 21613.000000 | 21613.000000 | 21613.000000 |
| mean | 4.580302e+09 | 5.400881e+05 | 3.370842 | 2.114757 | 2079.899736 | 1.510697e+04 | 1.494309 | 0.007542 | 0.234303 | 3.409430 | 7.656873 | 1788.390691 | 291.509045 | 1971.005136 | 84.402258 | 98077.939805 | 47.560053 | -122.213896 | 1986.552492 | 12768.455652 |
| std | 2.876566e+09 | 3.671272e+05 | 0.930062 | 0.770163 | 918.440897 | 4.142051e+04 | 0.539989 | 0.086517 | 0.766318 | 0.650743 | 1.175459 | 828.090978 | 442.575043 | 29.373411 | 401.679240 | 53.505026 | 0.138564 | 0.140828 | 685.391304 | 27304.179631 |
| min | 1.000102e+06 | 7.500000e+04 | 0.000000 | 0.000000 | 290.000000 | 5.200000e+02 | 1.000000 | 0.000000 | 0.000000 | 1.000000 | 1.000000 | 290.000000 | 0.000000 | 1900.000000 | 0.000000 | 98001.000000 | 47.155900 | -122.519000 | 399.000000 | 651.000000 |
| 25% | 2.123049e+09 | 3.219500e+05 | 3.000000 | 1.750000 | 1427.000000 | 5.040000e+03 | 1.000000 | 0.000000 | 0.000000 | 3.000000 | 7.000000 | 1190.000000 | 0.000000 | 1951.000000 | 0.000000 | 98033.000000 | 47.471000 | -122.328000 | 1490.000000 | 5100.000000 |
| 50% | 3.904930e+09 | 4.500000e+05 | 3.000000 | 2.250000 | 1910.000000 | 7.618000e+03 | 1.500000 | 0.000000 | 0.000000 | 3.000000 | 7.000000 | 1560.000000 | 0.000000 | 1975.000000 | 0.000000 | 98065.000000 | 47.571800 | -122.230000 | 1840.000000 | 7620.000000 |
| 75% | 7.308900e+09 | 6.450000e+05 | 4.000000 | 2.500000 | 2550.000000 | 1.068800e+04 | 2.000000 | 0.000000 | 0.000000 | 4.000000 | 8.000000 | 2210.000000 | 560.000000 | 1997.000000 | 0.000000 | 98118.000000 | 47.678000 | -122.125000 | 2360.000000 | 10083.000000 |
| max | 9.900000e+09 | 7.700000e+06 | 33.000000 | 8.000000 | 13540.000000 | 1.651359e+06 | 3.500000 | 1.000000 | 4.000000 | 5.000000 | 13.000000 | 9410.000000 | 4820.000000 | 2015.000000 | 2015.000000 | 98199.000000 | 47.777600 | -121.315000 | 6210.000000 | 871200.000000 |
plt.hist(df['price'], bins=100)
plt.title('Стоимость недвижимости')
plt.xlabel('Стоимость')
plt.ylabel('Количество');
plt.figure(figsize=(6,4))
plt.boxplot(df['price'])
plt.title('Cтоимость недвижимости')
plt.ylabel('Cтоимость');
Вывод: Большая часть домов находится в диапозоне стоимости от 0,2x10^6 до 1,1х10^6
plt.figure(figsize=(6,4))
plt.hist(df['sqft_living'], bins=100)
plt.title('Распределение жилой площади')
plt.xlabel('Жилая площадь')
plt.ylabel('Количество');
plt.figure(figsize=(6,4))
plt.boxplot(df['sqft_living'])
plt.title('Жилая площадь')
plt.ylabel('Жилая площадь');
Вывод: Большая часть домов находятся в диапозоне от 400 до 4000 кв. фута по жилой площади
plt.figure(figsize=(6,4))
plt.hist(df['yr_built'], bins=10)
plt.title('Распределение по году постройки')
plt.xlabel('Год постройки')
plt.ylabel('Количество');
plt.figure(figsize=(6,4))
plt.boxplot(df['yr_built'])
plt.title('Распределение по году постройки')
plt.ylabel('Год постройки');
Выводы: Большая часть домов построено с 1950 по 2000 гг. Есть большой пик новых домов построенных в середине 2000
dataFGW = df['waterfront'].value_counts()
dataFGW
waterfront 0 21450 1 163 Name: count, dtype: int64
plt.pie(dataFGW.values, autopct='%.1f%%', labels=dataFGW.index);
plt.title('Распределение домов по виду на набережную')
Text(0.5, 1.0, 'Распределение домов по виду на набережную')
Выводы: Подавляющие большинство домов 99,2% без вида на набережную
dataFGF = df['floors'].value_counts().reset_index()
dataFGF.head()
| floors | count | |
|---|---|---|
| 0 | 1.0 | 10680 |
| 1 | 2.0 | 8241 |
| 2 | 1.5 | 1910 |
| 3 | 3.0 | 613 |
| 4 | 2.5 | 161 |
total = sum(dataFGF['count'])
labelsPie = [f'{n} {v/total:.2%}' for n,v in zip(dataFGF['floors'], dataFGF['count'])]
labelsPie
plt.pie(dataFGF['count'])
plt.title('Распределение домов по количеству этажей, %')
plt.xlabel("")
plt.legend(labels = labelsPie)
<matplotlib.legend.Legend at 0x2584b290850>
Выводы: Большинство домов одно и двух этажной застройки
dataFGС = df['condition'].value_counts()
dataFGС
condition 3 14031 4 5679 5 1701 2 172 1 30 Name: count, dtype: int64
plt.figure(figsize=(6,4))
plt.bar(dataFGС.index, dataFGС.values)
plt.title('Распределение состояния домов')
plt.xlabel('Состояние')
plt.ylabel('Количество, шт')
Text(0, 0.5, 'Количество, шт')
Выводы: Большинство домов c состоянием 3. Домов в плохом состоянии 1 и 2 меньшинство
- Анализ сделайте в формате storytelling: дополнить каждый график письменными выводами и наблюдениями.
Для начала проверим корреляцию характеристик построив матрицу корреляций
corr_matrix = df.corr(numeric_only=True)
corr_matrix = np.round(corr_matrix, 1)
corr_matrix[np.abs(corr_matrix) < 0.3] = 0
corr_matrix
| id | price | bedrooms | bathrooms | sqft_living | sqft_lot | floors | waterfront | view | condition | grade | sqft_above | sqft_basement | yr_built | yr_renovated | zipcode | lat | long | sqft_living15 | sqft_lot15 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| id | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| price | 0.0 | 1.0 | 0.3 | 0.5 | 0.7 | 0.0 | 0.3 | 0.3 | 0.4 | 0.0 | 0.7 | 0.6 | 0.3 | 0.0 | 0.0 | 0.0 | 0.3 | 0.0 | 0.6 | 0.0 |
| bedrooms | 0.0 | 0.3 | 1.0 | 0.5 | 0.6 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.4 | 0.5 | 0.3 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.4 | 0.0 |
| bathrooms | 0.0 | 0.5 | 0.5 | 1.0 | 0.8 | 0.0 | 0.5 | 0.0 | 0.0 | 0.0 | 0.7 | 0.7 | 0.3 | 0.5 | 0.0 | 0.0 | 0.0 | 0.0 | 0.6 | 0.0 |
| sqft_living | 0.0 | 0.7 | 0.6 | 0.8 | 1.0 | 0.0 | 0.4 | 0.0 | 0.3 | 0.0 | 0.8 | 0.9 | 0.4 | 0.3 | 0.0 | 0.0 | 0.0 | 0.0 | 0.8 | 0.0 |
| sqft_lot | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.7 |
| floors | 0.0 | 0.3 | 0.0 | 0.5 | 0.4 | 0.0 | 1.0 | 0.0 | 0.0 | -0.3 | 0.5 | 0.5 | 0.0 | 0.5 | 0.0 | 0.0 | 0.0 | 0.0 | 0.3 | 0.0 |
| waterfront | 0.0 | 0.3 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1.0 | 0.4 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| view | 0.0 | 0.4 | 0.0 | 0.0 | 0.3 | 0.0 | 0.0 | 0.4 | 1.0 | 0.0 | 0.3 | 0.0 | 0.3 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.3 | 0.0 |
| condition | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | -0.3 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | -0.4 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| grade | 0.0 | 0.7 | 0.4 | 0.7 | 0.8 | 0.0 | 0.5 | 0.0 | 0.3 | 0.0 | 1.0 | 0.8 | 0.0 | 0.4 | 0.0 | 0.0 | 0.0 | 0.0 | 0.7 | 0.0 |
| sqft_above | 0.0 | 0.6 | 0.5 | 0.7 | 0.9 | 0.0 | 0.5 | 0.0 | 0.0 | 0.0 | 0.8 | 1.0 | 0.0 | 0.4 | 0.0 | -0.3 | 0.0 | 0.3 | 0.7 | 0.0 |
| sqft_basement | 0.0 | 0.3 | 0.3 | 0.3 | 0.4 | 0.0 | 0.0 | 0.0 | 0.3 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| yr_built | 0.0 | 0.0 | 0.0 | 0.5 | 0.3 | 0.0 | 0.5 | 0.0 | 0.0 | -0.4 | 0.4 | 0.4 | 0.0 | 1.0 | 0.0 | -0.3 | 0.0 | 0.4 | 0.3 | 0.0 |
| yr_renovated | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| zipcode | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | -0.3 | 0.0 | -0.3 | 0.0 | 1.0 | 0.3 | -0.6 | -0.3 | 0.0 |
| lat | 0.0 | 0.3 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.3 | 1.0 | 0.0 | 0.0 | 0.0 |
| long | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.3 | 0.0 | 0.4 | 0.0 | -0.6 | 0.0 | 1.0 | 0.3 | 0.3 |
| sqft_living15 | 0.0 | 0.6 | 0.4 | 0.6 | 0.8 | 0.0 | 0.3 | 0.0 | 0.3 | 0.0 | 0.7 | 0.7 | 0.0 | 0.3 | 0.0 | -0.3 | 0.0 | 0.3 | 1.0 | 0.0 |
| sqft_lot15 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.7 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.3 | 0.0 | 1.0 |
sns.heatmap(corr_matrix, annot = True, linewidths=0.5, cmap='coolwarm');
По тепловой карте видно что цена явно зависит от размера жилой площади (sqft_living), оценки недвижимости (grade) и возможно площади выше уровня земли (sqft_above) и Кв. метры жилой площади у 15 соседей (sqft_living15). Так как жилая площадь входит состав параметров sqft_above, sqft_living15 их можно не учитывать.
Отобразим взаимозависимости этих величин на графиках
cols = ['price', 'sqft_living', 'grade']
namesax = ['Цена', 'Площадь', 'Оценка']
g = sns.PairGrid(df[cols], corner=True)
g.map(sns.scatterplot)
g.fig.suptitle("Зависимости характеристик", y=1)
Text(0.5, 1, 'Зависимости характеристик')
По графмикам можно также предположить наличие зависимостей стоимости от жилой площади и оценки
Взаимное распределение всех трех показателей показано на графике ниже.
p = sns.jointplot(x=df['price'], y=df['sqft_living'], hue=df['grade']);
p.fig.suptitle('Взаимное распределение');
На графике видно что все три характеристики взаимно растут с увеличением одной из них.
Посмотрим статистические характеристики данных по цене:
df['price'].describe()
count 2.161300e+04 mean 5.400881e+05 std 3.671272e+05 min 7.500000e+04 25% 3.219500e+05 50% 4.500000e+05 75% 6.450000e+05 max 7.700000e+06 Name: price, dtype: float64
Используя их разобъем стоимость домов на три блока:
df.loc[df['price']<321950, 'Price_grade'] = 'Низкая цена'
df.loc[(df['price']>=321950) & ((df['price']<=645000)), 'Price_grade'] = 'Средняя цена'
df.loc[df['price']>645000, 'Price_grade'] = 'Высокая цена'
df['Price_grade'].value_counts()
Price_grade Средняя цена 10837 Низкая цена 5403 Высокая цена 5373 Name: count, dtype: int64
Построим блочную диаграмму размаха и посмотрим как распределена характеристика жилой площади по ценовым сегментам.
plt.figure(figsize=(10, 6))
# sns.boxplot(x=df['sqft_living'], y=df['Price_grade'], showfliers=False)
sns.boxplot(x=df['sqft_living'], y=df['Price_grade'])
plt.xlabel('Жилая площадь')
plt.ylabel('Ценовой сегмент')
plt.title('Зависимость цены от жилой площади по сегментам');
Судя по графику, хотя прослеживается зависимость, что чем выше цена тем более высокая жилая площадь, но количество выбросов длинна "усов" говорят о том что эта характеристика не определяющая и должны быть другие характеристики влияющие на стоимость жилья.
Построим также блочную диаграмму размаха и для оценки недвижимости (grade) по ценовым сегментам.
plt.figure(figsize=(10, 6))
sns.boxplot(x=df['grade'], y=df['Price_grade'])
plt.xlabel('Оценка недвижимости')
plt.ylabel('Ценовой сегмент')
plt.title('Зависимость цены от оценки недвижимости');
Также как и для жилой площади на графике видно что величина характеристики сдвигается в право с увеличением ценового сегмента, но также есть выбросы и длинные "усы", особенно для Высокоценового сегмента.
Теперь попрообуем наоборот разбить характеристику оценки на три категории: Низкое, Среднее, Высокое качество.
df.loc[df['grade']<=3, 'grade_category'] = 'Низкое качество'
df.loc[(df['grade']>3) & ((df['grade']<=10)), 'grade_category'] = 'Среднее Качество'
df.loc[df['grade']>=11, 'grade_category'] = 'Высокое качество'
И построим ящик с усами для Сегементов по качеству и цены
plt.figure(figsize=(10, 6))
# sns.boxplot(x=df['price'], y=df['grade_category'], showfliers=False)
sns.boxplot(x=df['price'], y=df['grade_category'])
plt.xlabel('Price')
plt.ylabel('grade_category')
plt.title('Зависимость цены от качества дизайна и архитектуры');
По графику видно, что низко оцененные дома в целом целиком поподают в область низкой стоимости. По среднему сегменту видно что есть дома на цену которых влияют другие характеристики. Но особенно это заметно по сегменту высокого качества где разброс цен очень велик.
Можно предположить что на цену домов может влиять также расположение домов в определенных зонах. Для этого построим распределение домов по координатам долготы/широты. Судя по этим параметрам наши данные относятся к городу Сиэтл и его окрестностям.
min_long = -123
max_long = -122
min_lat = 47
max_lat = 48
import matplotlib.image as img
seatle_map = img.imread('seatle_map3.PNG')
Построим для начала график по всем данным, расположив цену домов на цветовой шкале.
plt.figure(figsize=(12,9))
sc= plt.scatter(df['long'], df['lat'], alpha=0.5, c=df['price'], cmap='plasma')
# sc=sns.scatterplot(data=df, y=df['lat'], x=df['long'], hue=df['price'])
plt.imshow(seatle_map, extent=[min_long, max_long, min_lat, max_lat], alpha=0.9)
plt.colorbar(sc)
plt.xlabel('Долгота')
plt.ylabel('Широта')
plt.title('География домов (общая)');
Уже можно видеть что наибольшая стоимость домов находится ближе к берегам на севере. Но воспользумемя разбивкой на сегменты чтобы увидеть больше закономерностей
Возьмем средний ценовой сегмент
dfMeanPrice = df[df['Price_grade']=='Средняя цена']
dfMeanPrice.shape
(10837, 23)
plt.figure(figsize=(12,9))
sc= plt.scatter(dfMeanPrice['long'], dfMeanPrice['lat'], alpha=0.6, c=dfMeanPrice['price'], cmap='plasma')
plt.imshow(seatle_map, extent=[min_long, max_long, min_lat, max_lat], alpha=0.9)
plt.colorbar(sc)
plt.xlabel('Долгота')
plt.ylabel('Широта')
plt.title('Георафия домов (Средний ценовой сегмент)');
На графике четко видна область с домами с более высокой ценой. Посмотрим теперь на карте распределение домов по характеристике жилой площади в этом же ценовом сегменте.
plt.figure(figsize=(12,9))
sc= plt.scatter(dfMeanPrice['long'], dfMeanPrice['lat'], alpha=0.6, c=dfMeanPrice['sqft_living'], cmap='plasma')
plt.imshow(seatle_map, extent=[min_long, max_long, min_lat, max_lat], alpha=0.9)
plt.colorbar(sc)
plt.xlabel('Долгота')
plt.ylabel('Широта')
plt.title('Георафия домов /к жилой площади (Средний ценовой сегмент)');
По графику видно что в области где самая высокая цена, жилая площадь меньше. Это легко объяснить так как для среднего сегмента мы отбросили самые высокие и самые низкие данные, а размер жилой площади все же влияет на цену, поэтому для престижных райнов для домов в среднем ценовом сегменте жилая площадь ниже, иначе бы их стоимость выпала за рамки данного сегмента.
Посмотрим теперь как для среднего сегмента распределена характеристика оценки архитектуры и дизайна
plt.figure(figsize=(12,9))
sc= plt.scatter(dfMeanPrice['long'], dfMeanPrice['lat'], alpha=0.6, c=dfMeanPrice['grade'], cmap='plasma')
plt.imshow(seatle_map, extent=[min_long, max_long, min_lat, max_lat], alpha=0.9)
plt.colorbar(sc)
plt.xlabel('Долгота')
plt.ylabel('Широта')
plt.title('Георафия домов /к оценке архитектуры и дизайна (Средний ценовой сегмент)');
Здесь прослеживается таже картина что и для жилой площади, пусть и менее выражено. Оценка домов в "престижных" районах ниже, благодаря чему дома и попали в средний сегмент.
Посмотрим теперь сегмент с высокой ценой
dfHightPrice = df[df['Price_grade']=='Высокая цена']
dfHightPrice.shape
(5373, 23)
plt.figure(figsize=(12,9))
sc= plt.scatter(dfHightPrice['long'], dfHightPrice['lat'], alpha=0.6, c=dfHightPrice['price'], cmap='plasma')
# sc=sns.scatterplot(data=df, y=df['lat'], x=df['long'], hue=df['price'])
plt.imshow(seatle_map, extent=[min_long, max_long, min_lat, max_lat], alpha=0.9)
plt.colorbar(sc)
plt.xlabel('Долгота')
plt.ylabel('Широта')
plt.title('География домов/цена (сегмент Высокая цена)');
Здесь сразу можно видеть "престижные" районы с высокой ценой, которые выделялись светлым цветом в среднем сегменте
Посиотрим и на нижний ценовой сегмент
dfLowPrice = df[df['Price_grade']=='Низкая цена']
dfLowPrice.shape
(5403, 23)
plt.figure(figsize=(12,9))
sc= plt.scatter(dfLowPrice['long'], dfLowPrice['lat'], alpha=0.6, c=dfLowPrice['price'], cmap='plasma')
plt.imshow(seatle_map, extent=[min_long, max_long, min_lat, max_lat], alpha=0.9)
plt.colorbar(sc)
plt.xlabel('Долгота')
plt.ylabel('Широта')
plt.title('География домов/цена (сегмент низкая цена)');
Здесь четко видно как дома с низкой ценой "обтекают" престижны район на севере на берегу и заполняет области на юге и севернее реки.
Было проведено исследование как характеристики недвижимости влияют на ее стоимость. Вероятно для полноценного разбора неободимо было бы использовать регресионный анализ или другие математисеские/статистические методы.